<?php
declare(strict_types=1);

namespace Dreamcat\ConfigClient\Api\Impl;

use Dreamcat\ConfigClient\Api\ApolloServerInterface;
use Dreamcat\ConfigClient\Popo\ApolloConfigResult;
use Dreamcat\ConfigClient\Popo\Enum\QueryStatus;
use Dreamcat\Http\EnhanceClient\Share\RestHttpClient;
use Psr\Http\Client\ClientExceptionInterface;

/**
 * apollo服务器对象
 * @author vijay
 */
class ApolloServer implements ApolloServerInterface
{
    /** @var string 服务器地址 */
    private $serverUrl;
    /** @var int 常规查询超时时长，ms */
    private $queryTimeoutMs = 500;
    /** @var int long pull的超时时长 */
    private $pullWaitTimeoutMs = 70000;
    /** @var RestHttpClient  */
    private $httpClient;

    /**
     * ApolloServer constructor.
     * @param string $serverUrl
     */
    public function __construct(string $serverUrl)
    {
        $this->serverUrl = $serverUrl;
        $this->httpClient = new RestHttpClient();
    }

    /**
     * @return string 服务器地址
     */
    public function getServerUrl(): string
    {
        return $this->serverUrl;
    }

    /**
     * @param string $serverUrl 服务器地址
     * @return static 对象本身
     */
    public function setServerUrl(string $serverUrl): ApolloServer
    {
        $this->serverUrl = $serverUrl;
        return $this;
    }

    /**
     * @return int 常规查询超时时长
     */
    public function getQueryTimeoutMs(): int
    {
        return $this->queryTimeoutMs;
    }

    /**
     * @param int $queryTimeoutMs 常规查询超时时长
     * @return static 对象本身
     */
    public function setQueryTimeoutMs(int $queryTimeoutMs): ApolloServer
    {
        if ($queryTimeoutMs >= 0) {
            $this->queryTimeoutMs = $queryTimeoutMs;
        }
        return $this;
    }

    /**
     * @return int long pull的超时时长
     */
    public function getPullWaitTimeoutMs(): int
    {
        return $this->pullWaitTimeoutMs;
    }

    /**
     * @param int $pullWaitTimeoutMs long pull的超时时长，ms，必须大于65000
     * @return static 对象本身
     */
    public function setPullWaitTimeoutMs(int $pullWaitTimeoutMs): ApolloServer
    {
        if ($pullWaitTimeoutMs > 65000) {
            $this->pullWaitTimeoutMs = $pullWaitTimeoutMs;
        }
        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getConfig(
        string $appid,
        string $clusterName,
        string $namespaceName,
        string $ip = null,
        string $releaseKey = null
    ): ApolloConfigResult {
        $url = "{$this->serverUrl}/configs/{$appid}/{$clusterName}/{$namespaceName}";
        $params = [];
        if ($ip !== null) {
            $params["ip"] = $ip;
        }
        if ($releaseKey !== null) {
            $params["releaseKey"] = $releaseKey;
        }
        if ($params) {
            $url .= "?" . http_build_query($params);
        }
        $request = $this->httpClient->getRequestFactory()->createRequest("GET", $url);
        $result = new ApolloConfigResult();
        $result->setAppId($appid)
            ->setCluster($clusterName)
            ->setNamespaceName($namespaceName);
        try {
            $response = $this->httpClient->getHttpClient()->sendRequest($request, $this->queryTimeoutMs);
            switch ($response->getStatusCode()) {
                case 200:
                    /** @var ApolloConfigResult $result */
                    $result = $this->httpClient->formatOutput($response, ApolloConfigResult::class);
                    $result->setQueryStatus(QueryStatus::CHANGED());
                    break;
                case 304:
                    $result->setQueryStatus(QueryStatus::NO_CHANGE());
                    break;
                default:
                    $result->setQueryStatus(QueryStatus::FAILED());
            }
        } catch (ClientExceptionInterface $e) {
            $result->setQueryStatus(QueryStatus::FAILED());
        }
        return $result;
    }

    /**
     * @inheritDoc
     */
    public function notifications(string $appid, string $clusterName, array $notifications): array
    {
        if (!$notifications) {
            return [];
        }
        $params = [
            "appId" => $appid,
            "cluster" => $clusterName,
            "notifications" => [],
        ];
        foreach ($notifications as $name => $id) {
            $params["notifications"][] = [
                "namespaceName" => $name,
                "notificationId" => intval($id),
            ];
        }
        $params["notifications"] = json_encode($params["notifications"]);
        $request = $this->httpClient->getRequestFactory()
            ->createRequest(
                "GET",
                "{$this->serverUrl}/notifications/v2?" . http_build_query($params)
            );

        try {
            $response = $this->httpClient->getHttpClient()->sendRequest($request, $this->pullWaitTimeoutMs);
            if ($response->getStatusCode() == 200) {
                $data = $this->httpClient->getResponseDecoder()->decodeResponse($response->getBody()->getContents());
                $result = [];
                foreach ($data as $datum) {
                    $result[$datum["namespaceName"]] = $datum["notificationId"];
                }
                return $result;
            } else {
                return [];
            }
        } catch (ClientExceptionInterface $e) {
            return [];
        }
    }
}

# end of file
